#include <avr/io.h>
#include "enc28j60.h"
#include "spi.h"

//Global variabel
unsigned char volatile LastBankUsed = 4;
unsigned int volatile NextPacketPtr;

void enc28j60WriteOp(unsigned char operation, unsigned char addr, unsigned char data)
{
    //set chip select pin on the ENC28J60 (active low)
    ENC28J60_CLEAR_CS_PIN;
                                                            
    //write opcode(operation) and address 
    SPI_transmit(operation | (addr & ADDR_MASK));
    //write data
    SPI_transmit(data);
    
    //release chip select pin on the ENC28J60 (active low)
    ENC28J60_SET_CS_PIN;       
}

void enc28j60Write(unsigned char addr, unsigned char data)
{
	// set the bank
	enc28j60SetBank(addr);
	// do the write
	enc28j60WriteOp(ENC28J60_WRITE_CTRL_REG, addr, data);
}

unsigned char enc28j60ReadOp(unsigned char operation, unsigned char addr)
{
   unsigned char data;

   
    //set chip select pin on the ENC28J60 (active low)
    ENC28J60_CLEAR_CS_PIN;

    //write opcode(operation) and address 
    SPI_transmit(operation | (addr & ADDR_MASK));

	if( addr & SPRD_MASK )
	{
		/*If the address specifies is on of the MAC or MII registers,
		  a dummy byte will first be shifted out on the SO pin*/
	    SPI_transmit(0);		
	}

    //read data from address
    data = SPI_read(); 
    
    //release chip select pin on the ENC28J60 (active low)
	asm("NOP");
    ENC28J60_SET_CS_PIN;
    
    return data;      
}

unsigned char enc28j60Read(unsigned addr)
{
	// set the bank
	enc28j60SetBank(addr);
	// do the read
	return enc28j60ReadOp(ENC28J60_READ_CTRL_REG, addr);
}

void enc28j60SetBank(unsigned char addr)
{
    if((addr & BANK_MASK) != LastBankUsed)
    {
        //clear bank address
        enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, BANK_ADDR_MASK);
        //set bank addres
        enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, (addr & BANK_MASK)>>5); 
        LastBankUsed = (addr & BANK_MASK);
    }
}

void enc28j60SoftReset(void)
{
    //set chip select pin on the ENC28J60 (active low)
    ENC28J60_CLEAR_CS_PIN;
    
    // perform system reset
    SPI_transmit(ENC28J60_SOFT_RESET); 

    //set chip select pin on the ENC28J60 (active low)
    ENC28J60_SET_CS_PIN;
}

void enc28j60PhyWrite(unsigned char address, unsigned int data)
{
	// set the PHY register address
	enc28j60Write(MIREGADR, address);
	
	// write the PHY data
	enc28j60Write(MIWRL, data);	
	enc28j60Write(MIWRH, data>>8);

	// wait until the PHY write completes
	while(enc28j60Read(MISTAT) & MISTAT_BUSY);
	{}
}

unsigned int enc28j60PhyRead(unsigned char address)
{
	unsigned int data;

    // Write the address of the PHY register to read from
	enc28j60Write(MIREGADR, address); 
    // Set the PHY register to read and the data is placed in MIRD(L and H)
	enc28j60Write(MICMD, MICMD_MIIRD);

	// wait until the PHY read completes
	while(enc28j60Read(MISTAT) & MISTAT_BUSY); 
    {}

	// Clear the MICMD register
	enc28j60Write(MICMD, 0x00);
	
	// read the data value
	data  = enc28j60Read(MIRDL);
	data |= enc28j60Read(MIRDH);
	// return the data
	return data;
}

void enc28j60Init(u8 *pHa, u8 opts)
{
	// set chip select pin on the ENC28J60
	ENC28J60_SET_CS_PIN;
	
	// initialize the spi communication
	init_SPI();

	// perform system reset
    enc28j60SoftReset();
    
    //wait for the PHY is ready
    //ESTAT_CLKRDY is 1 when ready
    while(!(enc28j60Read(ESTAT) & ESTAT_CLKRDY));
    {}
    
    //initialize Bank 0 --------------------------------
    
    NextPacketPtr = RXSTART_INIT;

    //set receive buffer start register
	enc28j60Write(ERXSTL, RXSTART_INIT&0xFF);
	enc28j60Write(ERXSTH, RXSTART_INIT>>8); 
	// set receive buffer end register
	// ERXND defaults to 0x1FFF (end of ram)
	enc28j60Write(ERXNDL, RXSTOP_INIT&0xFF);
	enc28j60Write(ERXNDH, RXSTOP_INIT>>8);  

	// set Buffer Read Pointer register
	enc28j60Write(ERDPTL, RXSTART_INIT&0xFF);
	enc28j60Write(ERDPTH, RXSTART_INIT>>8);
    
    //set receive buffer read pointer register
	enc28j60Write(ERXRDPTL, RXSTOP_INIT&0xFF);
	enc28j60Write(ERXRDPTH, RXSTOP_INIT>>8);
       
    /*
    Any space within the 8-Kbyte memory, which is not
    programmed as part of the receive FIFO buffer, is
    considered to be the transmit buffer
    */    
	enc28j60Write(ETXSTL, TXSTART_INIT&0xFF);
	enc28j60Write(ETXSTH, TXSTART_INIT>>8);
    
    //initialize Bank 2 --------------------------------       
    
	// enable MAC receive 
    /* 
    TXPAUS: Pause Control Frame Transmission Enable bit
    1 = Allow the MAC to transmit pause control frames (needed for flow control in full duplex)
    0 = Disallow pause frame transmissions
    RXPAUS: Pause Control Frame Reception Enable bit
    1 = Inhibit transmissions when pause control frames are received (normal operation)
    0 = Ignore pause control frames which are received
    PASSALL: Pass All Received Frames Enable bit
    1 = Control frames received by the MAC will be written into the receive buffer if not filtered out
    0 = Control frames will be discarded after being processed by the MAC (normal operation)
    MARXEN: MAC Receive Enable bit
    1 = Enable packets to be received by the MAC
    0 = Disable packet reception    
    */
	enc28j60Write(MACON1, MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS);

	// enable automatic padding and CRC operations 
    /*
    MACON3_PADCFG0 = 0x20   do: All short frames will be zero padded to 60 bytes and a valid CRC will then be appended
    MACON3_TXCRCEN = 0x10   do: MAC will append a valid CRC to all frames transmitted regardless of PADCFG bits. TXCRCEN 
    MACON3_FRMLNEN = 0x02   do: The type/length field of transmitted and received frames will be checked. If it represents a length, the
                                frame size will be compared and mismatches will be reported in the transmit/receive status vector.
    */
	enc28j60Write(MACON3, MACON3_PADCFG0|MACON3_TXCRCEN);
	// set inter-frame gap (non-back-to-back)
	enc28j60Write(MAIPGL, 0x12);
	enc28j60Write(MAIPGH, 0x0C);
	// set inter-frame gap (back-to-back)
    /*
    0x15 when Full-Duplex mode is used and 0x12 when Half-Duplex mode is used    
    */
	enc28j60Write(MABBIPG, 0x12);
	// Set the maximum packet size which the controller will accept, need to be less than 1518 bytes
	enc28j60Write(MAMXFLL, MAX_FRAMELEN&0xFF);	
	enc28j60Write(MAMXFLH, MAX_FRAMELEN>>8);    
   
    //initialize Bank 3 --------------------------------  
    
	// write MAC address
	// NOTE: MAC address in ENC28J60 is byte-backward
	enc28j60Write(MAADR6, pHa[5]);
	enc28j60Write(MAADR5, pHa[4]);
	enc28j60Write(MAADR4, pHa[3]);
	enc28j60Write(MAADR3, pHa[2]);
	enc28j60Write(MAADR2, pHa[1]);
	enc28j60Write(MAADR1, pHa[0]);
 
    //--------------------------------------------------

	// no loopback of transmitted frames
	enc28j60PhyWrite(PHCON2, PHCON2_HDLDIS);

    /*
    UCEN: Packets destination address matching the local MAC address
    ANDOR: AND/OR Filter Select bit
            1 = AND: Packets will be rejected unless all enabled filters accept the packet
            0 = OR: Packets will be accepted unless all enabled filters reject the packet
    CRCEN: All packets with an invalid CRC
    BCEN: Broadcast Filter Enable bit: Destination address of FF-FF-FF-FF-FF-FF
    */
    // set the ethernet receive filter
	if(opts) // Promiscuous mode: (network sniffer mode)
	{
		enc28j60Write(ERXFCON,0); //accept all packets		
	}
	else	// Normal mode
	{
		enc28j60Write(ERXFCON, ERXFCON_UCEN|ERXFCON_CRCEN|ERXFCON_BCEN);
	}
    
    /*
    RXEN: Packets which pass the current filter configuration will be written into the receive buffer
    */
	// enable packet reception
	enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN);      

	// change the LEDA configuration bits: the green led will now display transmit activity 
	enc28j60PhyWrite(PHLCON, 0x3122);
}

void enc28j60WriteBuffer(unsigned int length, unsigned char* data)
{
    unsigned int i;

    //set chip select pin on the ENC28J60 (active low)
    ENC28J60_CLEAR_CS_PIN;
                                                               
    //write opcode(WRITE_BUF_MEM)
    SPI_transmit(ENC28J60_WRITE_BUF_MEM);    
    
    //write data
    for(i = 0; i < length; i++)
    {
		SPI_transmit(*data++);
    }
    
    //release chip select pin on the ENC28J60 (active low)
    ENC28J60_SET_CS_PIN; 
}

void enc28j60ReadBuffer(unsigned int length, unsigned char* data)
{
    unsigned int i;

    //set chip select pin on the ENC28J60 (active low)
    ENC28J60_CLEAR_CS_PIN;

    //write opcode(ENC28J60_READ_BUF_MEM)
    SPI_transmit(ENC28J60_READ_BUF_MEM);
    
    //read data
    for(i = 0; i < length; i++)
    {
       *data++ = SPI_read();        
    }

    //release chip select pin on the ENC28J60 (active low)
    ENC28J60_SET_CS_PIN;
}

unsigned int enc28j60BeginPacketReceive(void)
{
	unsigned int len;
	unsigned char statusVec1, statusVec2  = 0;

	// Check the ethernet packet count is it over 0 a packages has been received
	if(enc28j60Read(EPKTCNT) == 0)
	{
		return 0;
	}

	// Set the read pointer to the start of the received packet
	enc28j60Write(ERDPTL, (NextPacketPtr));
	enc28j60Write(ERDPTH, (NextPacketPtr)>>8);
	// read the next packet pointer
	NextPacketPtr  = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0);
	NextPacketPtr |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8;

	// Read the receive status vector ------------------------------

	// read the packet length
	len  = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0);
	len |= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0)<<8;

	statusVec1  = enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0); // for debugging
	statusVec2	= enc28j60ReadOp(ENC28J60_READ_BUF_MEM, 0); // for debugging

	// see p.44 to see what the receive status vector contains 
	// -------------------------------------------------------------

	return len;
}

void enc28j60GetReceivedData(unsigned char * packet, unsigned int length)
{
	// copy the packet from the receive buffer
	enc28j60ReadBuffer(length, packet);
}

void enc28j60EndPacketReceive(void)
{	
	// Move the RX read pointer to the start of the next received packet 
	enc28j60Write(ERXRDPTL, (NextPacketPtr));
	enc28j60Write(ERXRDPTH, (NextPacketPtr)>>8);                                             
															        
    // This frees the memory we just read out 
	// decrement the packet counter and if it is 0, the receive packet interrupt flag(PKTIF) will be cleared
	enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC);
}

void enc28j60PacketSendSetUp(unsigned int length)
{
	// Is the previous transmission finished, if not wait
	while(enc28j60Read(ECON1) & ECON1_TXRTS)
	{}

	// Work around see ENC28J60 Silicon Errata point 12. 
	// Errata: Transmit Logic reset
	enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRST);
	enc28j60WriteOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRST);

	// set the ETXST to correspond to the start of the packet
	enc28j60Write(ETXSTL, TXSTART_INIT&0xff); 
	enc28j60Write(ETXSTH, TXSTART_INIT>>8);

	// Set the write pointer to start of transmit buffer area
	enc28j60Write(EWRPTL, TXSTART_INIT&0xff);
	enc28j60Write(EWRPTH, TXSTART_INIT>>8);

	// Set the ETXND to correspond to the end of the packet
	enc28j60Write(ETXNDL, (TXSTART_INIT+length));
	enc28j60Write(ETXNDH, (TXSTART_INIT+length)>>8);

	//write per-packet control byte
	// 0x00 = The values in MACON3 will be used to determine how the packet will be send
	// 0x0E = make zero padded to 60 bytes and a valid CRC will be calculated
	enc28j60WriteOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00);
}

void enc28j60PacketFill(unsigned char * packet, unsigned int length)
{

	// copy the packet into the transmit buffer
	enc28j60WriteBuffer(length, packet);
}

void enc28j60PacketSend(void)
{
	// send the contents of the transmit buffer onto the network
	enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS);
}

void enc28j60InitInterrupt(void)  // not in use
{

	DDRD = 0;

	// The falling edge of INT1 generates an interrupt request
	MCUCR |= 0x08;

	// External Interrupt Request 1 Enable
	GICR |= (1<<INT1); 

	asm("sei");
	
	// Set the global INT interrupt enable bit
	enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE);

	// Enable transmit interrupt: get at interrupt when the transmission is finished
	enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_TXIE);

	// Enable transmit Error interrupt
	enc28j60WriteOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_TXERIE);
}

